-module(agent_report).

-behaviour(gen_server).

%% API
-export([start_link/0]).
-export([report/0]).
-export([regist/1]).
%%-export([deploy/1]).
%%-export([undeploy/1]).
-export([test_query/0]).


%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
	 terminate/2, code_change/3]).
-include("agent.hrl").
-include_lib("stdlib/include/qlc.hrl").
-record(state, {
		 container ,    %%  container info
		 monitor_log_file,	%% monitor log file 
		 cpu       ,           %% 上一周期container cpu负荷
		 pid
		}).
test_query()->
	gen_server:call(agent_report,{test_query}).


report()->
	gen_server:cast(agent_report,{report}).

regist(Any)->
	gen_server:call(?MODULE,{regist,Any}).



%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

%%====================================================================
%% gen_server callbacks
%%====================================================================

%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%%                         {ok, State, Timeout} |
%%                         ignore               |
%%                         {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([]) ->
        error_logger:info_msg("starting agent_report .....~n"),
        %%todo最好是在depoy第一个应用的时候注册定时器
        case     timer:apply_interval(10000, ?MODULE , report, []) of
                {ok , _ } ->
                        case mnesia_op:get_agent_state() of
                           [] ->     
                            {ok, #state{
			        monitor_log_file=get_monitor_log(),
			        cpu =0 			     
                        	}};
                           [AgentState] ->
                             {ok, #state{
                                container=AgentState#agent_state.container,
                               monitor_log_file=get_monitor_log(),
                                pid=AgentState#agent_state.pid,   
                                cpu=AgentState#agent_state.cpu
                                        }}
                        end;	
                 {error , Reason } ->
                 error_logger:info_msg("starting agent_report error :~p~n",[Reason]),
                                {stop ,Reason } 
         end.

%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%%                                      {reply, Reply, State, Timeout} |
%%                                      {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, Reply, State} |
%%                                      {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call({test_query},_From,State)->

	{reply , State ,  State } ;

handle_call( {regist, {Container,Pid} } , _From , State ) ->
	{reply , ok , State#state{container=Container , pid=Pid     }} ;
handle_call(_Request, _From, State) ->
  	  Reply = ok,
   	 {reply, Reply, State}.



%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast({report} , State )->
%%	 error_logger:info_msg("begin report!~n"),
	 case mnesia:table_info(app, size) of
		0 ->
		        
	                { noreply , State } ;
	        _ ->
                	 AppRuntimes= parse_instance_runtimes(State),
                	 ContainerRuntime=parse_container_runtime(State),
			 send_report( State#state.pid,
                	                State#state.container,AppRuntimes,
                	                         ContainerRuntime),
                         Runtime=ContainerRuntime#container_runtime.runtime,             	                         
                	 {noreply,State#state{cpu=Runtime#runtime.cpu}} 
         end;
handle_cast(_Msg, State) ->
    {noreply, State}.

%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%%                                       {noreply, State, Timeout} |
%%                                       {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------

handle_info(Info, State) ->
     error_logger:info_msg("agent_report: recving a msg:~p~n",[Info]),
    {noreply, State}.

%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
    ok.

%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
parse_container_runtime(State)->
        Container=State#state.container,
	Result = trim_right(os:cmd( io_lib:format("sh script/monitor_container.sh ~s",[Container#container.home]) )),
        {Cpu,_}=string:to_integer(Result),
%%        Cpu = case    cpu_sup:avg5() of
%%                { error , Reason } ->
%%                        0;
%%                Any  ->
%%                        Any /256 
%%        end,
        #container_runtime{name=Container#container.name,runtime=#runtime{cpu=(Cpu*2+State#state.cpu)/3}}.

parse_instance_runtimes(State ) ->
        agent_httpd_proxy:get_stat_data(State#state.container,5000).

trim_right(Str)->
        case string:str(Str,"\n") of
                0 ->
                        Str;
                1 ->
                        "0"; 
                Index ->
                        string:substr(Str,1,Index-1)
        end.
	

send_report(Pid , Container ,  InstanceRuntimes , ContainerRuntime)->
%%        error_logger:info_msg("send_report InstanceRuntimes=~p~n",[InstanceRuntimes]),
        Apps = mnesia_op:lookup_app(),
	error_logger:info_msg("Apps,~p~n",[Apps]),
	Runtime=#runtime{},
        case mnesia_op:do(qlc:q([  { #instance { app_name= X#app.name ,  container_name = Container#container.name } ,
                                        Runtime } ||
                              X <- mnesia:table(app)])) of
                [] ->
                     	gen_server:cast( Pid,  { report, {[], ContainerRuntime}});
                ZeroInstanceRuntimes ->
  %%                   error_logger:info_msg("ZeroInstanceRuntimes=~p~n",[ZeroInstanceRuntimes]),
                        ZIRs = ets:new(?MODULE,[ordered_set,private]),  
                        ets:insert(ZIRs, ZeroInstanceRuntimes ),
 %%                     	FF=  fun (InstanceRuntime) ->
%%                                lists:member( (InstanceRuntime#instance_runtime.instance)#instance.app_name , AppNames)
 %%                       end,
 %%                       IRs=lists:filter(FF,InstanceRuntimes),
%%                      error_logger:info_msg("send_report :~p~n",[{report,{ IRs, ContainerRuntime}}]),
%%                        error_logger:info_msg("ZIRs :~p~n",[ets:tab2list(ZIRs)]),
                        lists:foreach(fun( InstanceRuntime) ->
    %%                             error_logger:info_msg("InstanceRuntime=~p~n",[InstanceRuntime]),
							 
                                case  ets:lookup(ZIRs , InstanceRuntime#instance_runtime.instance)    of
                                        [] ->
                                                error_logger:info_msg("the instance ~p has been undeployed~n" ,
                                                        [InstanceRuntime#instance_runtime.instance]) ;
                                        _ ->
                                                error_logger:info_msg("tempZirs before ~p~n",[ets:tab2list(ZIRs)]),
						error_logger:info_msg("Instance ~p ~p~n",[InstanceRuntime#instance_runtime.instance,InstanceRuntime#instance_runtime.runtime]),
						ets:insert(ZIRs , {InstanceRuntime#instance_runtime.instance,
							InstanceRuntime#instance_runtime.runtime } ),
						error_logger:info_msg("tempZIRS ~p~n",[ets:tab2list(ZIRs)])
                                end
                        
                                end ,  InstanceRuntimes ),                       
		%%				ets:insert(ZIRs, ZeroInstanceRuntimes ),
		
                        IRs=ets:tab2list(ZIRs),
			error_logger:info_msg("ZIRS:~p, IRs:~p~n",[ZIRs,IRs]),
                        ets:delete(ZIRs),
  %%                      error_logger:info_msg("send_report :~p~n",[{report,{ IRs, ContainerRuntime}}]),
                        gen_server:cast( Pid,  { report, {   lists:map(  fun({I,R} )->
																		  #instance_runtime{instance=I , runtime=R }
																		  end, IRs ), ContainerRuntime}})   
        end.
                                      
	

get_monitor_log()->
	case os:getenv("monitor.log") of false -> ?DEFAULT_MONITOR_LOG;	Any ->  Any end.



